今天我們來用 electron-forge 打包程式。
npm i -D @electron-forge/cli
然後執行:
npm exec --package=@electron-forge/cli -c "electron-forge import"
這樣它就會自動幫你把需要的都安裝好了。
你的 package.json 也會增加一些關於 electron-forge 的設定。
然後它會把之前寫的 start script 改掉,但不用擔心。
"scripts": {
    "start": "electron-forge start",
    "package": "electron-forge package",
    "make": "electron-forge make"
},
它會把 start package make 都弄好,開發時要啟動還是一樣 npm start 就可以了。
現在只需要執行 npm make 它就會幫你生成安裝檔了。
在 package.json 中我們可以看到有個 makers 在 config.forge 裡:
"makers": [
    {
        "name": "@electron-forge/maker-squirrel",
        "config": {
            "name": "click_serve"
        }
    },
    {
        "name": "@electron-forge/maker-zip",
        "platforms": [
            "darwin"
        ]
    },
    {
        "name": "@electron-forge/maker-deb",
        "config": {}
    },
    {
        "name": "@electron-forge/maker-rpm",
        "config": {}
    }
]
這就是它會打包的各種東東。
但要在「目標」的作業系統上打包喔。
這樣我們就打包好我們的程式了!

然後就可以雙擊安裝!
接著發現安裝時程式閃來閃去...
因為 Squirrel 會在安裝時開關幾次程式。
為了預防閃瞎使用者,我們用 electron-squirrel-startup 來阻止安裝時的啟動。
npm i electron-squirrel-startup
然後在 main.js 中 app.whenReady 的部分加上:
if (require("electron-squirrel-startup")) return;
在 createWindow 前結束就可以解決這個問題了。
還記得我們的程式預設的網站資料夾是程式執行位置旁的 www 吧。
我們不希望使用者剛開程式什麼都不設定直接執行的時候是顯示 Not Found。
所以我們會在安裝的目錄資加範例 www 資料夾。
理論上是可以在 package.json 中設定 afterExtract 來自動複製的。
但我不知道為什麼 Squirrel 沒包含它...
但沒關係,因為我們有 Source,所以我們在程式啟動時檢查資料夾存在與否,如果不存在就複製。
一樣是在 main.js 的 app.whenReady 中加上:
if (process.platform === "win32") copyExample();
其實就是遞迴複製檔案。
function copyExample() {
    const fs = require("fs");
    const path = require("path");
    if (!fs.existsSync(path.join(process.cwd(), "www")) && fs.existsSync(path.join(process.cwd(), "resources", "app", "www")))
        copyFolderSync(path.join(process.cwd(), "resources", "app", "www"), path.join(process.cwd()));
    function copyFileSync(source, target) {
        let targetFile = target;
        if (fs.existsSync(target) && fs.lstatSync(target).isDirectory()) targetFile = path.join(target, path.basename(source));
        fs.writeFileSync(targetFile, fs.readFileSync(source));
    }
    function copyFolderSync(source, target) {
        const targetFolder = path.join(target, path.basename(source));
        if (!fs.existsSync(targetFolder)) fs.mkdirSync(targetFolder);
        if (fs.lstatSync(source).isDirectory()) {
            fs.readdirSync(source).forEach((file) => {
                const curSource = path.join(source, file);
                if (fs.lstatSync(curSource).isDirectory()) copyFolderSync(curSource, targetFolder);
                else copyFileSync(curSource, targetFolder);
            });
        }
    }
}
這樣我們的程式在安裝後不設定啟動就會用我們的 Example 網站囉!

明天就是最後一天了!看看要不要簡單說一下 TypeScript 或 Deno。
以 10/12 20:00 ~ 10/13 20:00 文章觀看數增加值排名
+176 23 - 建立結構化的 Log (1/4) - Elastic Common Schema 結構化 Log 的規範
+127 [職場]新工程師報到,如何協助他成為有效戰力
+124 [訪談] APCS x 自學生 Jason
+124 除了刷題之外的事 - Software Engineering
+120 Day 24: 人工智慧在音樂領域的應用 (AI作曲- RNN作曲)
+119 Day 22: 人工智慧在音樂領域的應用 (AI作曲-基因演算法五 基於規則(Rule-Based)的Fitness Function)
+118 Day 27: 人工智慧在音樂領域的應用 (索尼-Flow Machine、谷歌-Magenta )
+112 Day 23: 人工智慧在音樂領域的應用 (AI作曲-基因演算法六  總要敬老尊賢吧?)
+107 LeetCode 雙刀流:62. Unique Paths
+99 Day_30:讓 Vite 來開啟你的Vue之 內牛滿面 的 完賽感言
再次恭喜 Sky 同學進到每日前十!